Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/三件套/JS/JavaScript

15 错误处理

10 分钟阅读 · Note

目录树 578 篇

            • 00 JavaScript学习地图
            • 01 JavaScript基础
            • 02 类型系统与类型转换
            • 03 Number与String
            • 04 Array数组
            • 05 Object对象
            • 06 集合与迭代
            • 07 函数、作用域与this
            • 08 原型、构造函数与类
            • 09 Generator与异步迭代
            • 10 异步编程
            • 11 浏览器与DOM
            • 12 二进制与文件
            • 13 标准内建对象
            • 14 模块化
            • 15 错误处理
            • 16 JSDoc
            • 90 JavaScript知识点总目录
            • 91 JavaScript修正与补充记录
          • 前端模块化
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗00 JavaScript学习地图反向链接↗90 JavaScript知识点总目录反向链接↗91 JavaScript修正与补充记录反向链接↗01 JavaScript基础同一路径↗02 类型系统与类型转换同一路径↗03 Number与String同一路径
  • 15 错误处理

15 错误处理

> Last Format Time:8/11/2026 21:45:41

本笔记由两个 JavaScript 教程目录中的同主题内容合并而成;全部旧知识保留在“旧笔记知识全集(按来源保留)”中,并由导航逐项索引。


当前前端开发关键要点

> [!important] 学习优先级:P0 > - 只捕获能够处理或补充上下文的错误;不要静默吞错,也不要把异常当普通分支滥用。 > - 异步错误需要在 Promise 链或 async/await 边界显式处理,并保留原始 cause 与诊断信息。 > - 区分用户可恢复错误、业务错误、程序错误和基础设施错误,分别设计反馈、重试与上报策略。 > - 前端错误边界、全局监听和监控平台是兜底,不替代局部正确处理。


知识点导航

[现代JS教程与阮一峰ES6 · 3错误处理/错误处理](./15 错误处理.md)

  • try...catch
  • Error对象
  • 自定义Error,Error的继承

[廖雪峰教程 · 8错误处理/8.0 简介](./15 错误处理.md)

  • 简介
  • try ... catch ... finally
  • 错误类型
  • 抛出错误

[廖雪峰教程 · 8错误处理/8.1 错误传播](./15 错误处理.md)

  • 错误传播

[廖雪峰教程 · 8错误处理/8.2 异步处理](./15 错误处理.md)

  • 异步处理

现代前端补充与纠错

> [!info] 修改标记 > - 【修正】:旧教程中错误、过时或容易误导的内容。 > - 【补充】:旧教程未覆盖、但当前前端开发需要掌握的内容。 > - 【修正代码】:替换或校正了旧代码示例。

> [!warning] 下方保留旧教程原文;现代错误处理还需要覆盖因果链、异步边界、取消和监控。

错误设计

  • 【补充】 只捕获能够恢复、补充上下文或转译的错误;不要空 catch 吞掉失败。
  • 【补充】 自定义错误类应保留 name、业务字段和原始原因,可使用 new Error(message, { cause }) 建立因果链。
  • 【修正】 finally 用于释放资源,不要在其中 return,否则可能覆盖原返回值或异常。
  • 【补充】 错误消息面向开发者,用户提示面向产品体验;不要把令牌、个人信息或服务端细节直接展示或上报。

异步错误

  • 【修正】 try/catch 只能捕获当前同步栈以及被 await 的拒绝;未返回/未等待的 Promise 不会被外层捕获。
  • 【补充】 window.unhandledrejection 和 Node 的对应事件只适合作为监控兜底,不能当正常控制流。
  • 【补充】 取消通常不是系统故障,应区分 AbortError、用户取消、超时、网络错误和 HTTP 业务错误。

旧笔记知识全集(按来源保留)

现代JS教程与阮一峰ES6 · 3错误处理/错误处理

try...catch

通常,如果发生错误,脚本就会“死亡”(立即停止),并在控制台将错误打印出来。

但是有一种语法结构 try...catch,它使我们可以“捕获(catch)”错误,因此脚本可以执行更合理的操作,而不是死掉。

语法:

try {
  // 代码...
} catch (err) {
  // 错误捕获
}

它按照以下步骤执行:

  1. 首先,执行 try {...} 中的代码。
  2. 如果这里没有错误,则忽略 catch (err):执行到 try 的末尾并跳过 catch 继续执行。
  3. 如果这里出现错误,则 try 执行停止,控制流转向 catch (err) 的开头。变量 err(我们可以使用任何名称)将包含一个error 对象,该对象包含了所发生事件的详细信息。 ![Pasted image 20250627170828](../../../../_assets/30e4dfa-Pasted image 20250627170828.png)
try {
    shit;
} catch (error) {
    console.log('error ' + error);//error ReferenceError: shit is not defined
}

try...catch 只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。 它必须是有效的 JavaScript 代码。

如果代码包含语法错误,那么 try..catch 将无法正常工作,例如含有不匹配的花括号

如果在“计划的(scheduled)”代码中发生异常,例如在 setTimeout 中,则 try...catch 不会捕获到异常:

try {
    setTimeout(function () {
        try {
            noSuchVariable;
        } catch (error) {
            console.log('内部');//
        }
    }, 1000);
} catch (err) {
    console.log("不工作");//
}

引擎是同步执行的,所以在执行到setTimeout的时候已经离开了try,为了捕获这个错误,try必须在setTimeout的内部,包裹住可能发生错误的位置。

Error对象

对于所有内建的 error,error 对象具有两个主要属性:

name Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"。

message 关于 error 的详细文字描述。

还有其他非标准的属性在大多数环境中可用。其中被最广泛使用和支持的是: stack 当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。

如果我们不需要 error 的详细信息,catch 也可以忽略它

使用throw会生成一个错误对象,并且抛出。在javascript中有很多的内建构造函数,Error,SyntaxError,ReferenceError,TypeError 等,用来构造一个错误对象。对于内建的 error(不是对于其他任何对象,仅仅是对于 error),name 属性刚好就是构造器的名字。message 则来自于参数(argument)。我们也可以自己来写:

let json = '{ "age": 30 }'; // 不完整的数据

try {

    let user = JSON.parse(json); // <-- 没有 error

    if (!user.name) {
        throw new SyntaxError("数据不全:没有 name"); // (*)
    }

    alert(user.name);

} catch (err) {
    alert("JSON Error: " + err.message); // JSON Error: 数据不全:没有 name
}

再次抛出:我们可以使用 instanceof 操作符判断错误类型

  1. Catch 捕获所有 error。
  2. 在 catch (err) {...} 块中,我们对 error 对象 err 进行分析。
  3. 如果我们不知道如何处理它,那我们就 throw err。
function readData() {
  let json = '{ "age": 30 }';

  try {
    // ...
    blabla(); // error!
  } catch (err) {
    // ...
    if (!(err instanceof SyntaxError)) {
      throw err; // 再次抛出(不知道如何处理它)
    }
  }
}

try {
  readData();
} catch (err) {
  alert( "External catch got: " + err ); // 捕获了它!
}

抛出的错误再次进行捕捉,使用外层的catch来处理

try...catch 结构可能还有一个代码子句(clause):finally。 如果它存在,它在所有情况下都会被执行:

  • try 之后,如果没有 error,
  • catch 之后,如果有 error。
try {
   ... 尝试执行的代码 ...
} catch (err) {
   ... 处理 error ...
} finally {
   ... 总是会执行的代码 ...
}

finally 子句适用于 try...catch 的 任何 出口。这包括显式的 return。

在下面这个例子中,在 try 中有一个 return。在这种情况下,finally 会在控制转向外部代码前被执行 :

function func() {

  try {
    return 1;

  } catch (err) {
    /* ... */
  } finally {
    alert( 'finally' );
  }
}

alert( func() ); // 先执行 finally 中的 alert,然后执行这个 alert

没有 catch 子句的 try...finally 结构也很有用。当我们不想在原地处理 error(让它们掉出去吧),但是需要确保我们启动的处理需要被完成时,我们应当使用它

function func() {
  // 开始执行需要被完成的操作(比如测量)
  try {
    // ...
  } finally {
    // 完成前面我们需要完成的那件事,即使 try 中的执行失败了
  }
}
自定义Error,Error的继承
class ValidationError extends Error {
  constructor(message) {
    super(message); // (1)
    this.name = "ValidationError"; // (2)
  }
}

try {
  let user = readUser('{ "age": 25 }');
} catch (err) {
  if (err instanceof ValidationError) {
    alert("Invalid data: " + err.message); // Invalid data: No field: name
  } else if (err instanceof SyntaxError) { // (*)
    alert("JSON Syntax Error: " + err.message);
  } else {
    throw err; // 未知的 error,再次抛出 (**)
  }
}

还有,没有学完。

廖雪峰教程 · 8错误处理/8.0 简介

简介

在执行JavaScript代码的时候,有些情况下会发生错误。

错误分两种,一种是程序写的逻辑不对,导致代码执行异常。例如:

let s = null;
let len = s.length; // TypeError:null变量没有length属性

对于这种错误,要修复程序。

一种是执行过程中,程序可能遇到无法预测的异常情况而报错,例如,网络连接中断,读取不存在的文件,没有操作权限等。

对于这种错误,我们需要处理它,并可能需要给用户反馈。

错误处理是程序设计时必须要考虑的问题。对于C这样贴近系统底层的语言,错误是通过错误码返回的:

int fd = open("/path/to/file", O_RDONLY);
if (fd == -1) {
    printf("Error when open file!");
} else {
    // TODO
}

通过错误码返回错误,就需要约定什么是正确的返回值,什么是错误的返回值。上面的open()函数约定返回-1表示错误。

显然,这种用错误码表示错误在编写程序时十分不便。

因此,高级语言通常都提供了更抽象的错误处理逻辑try ... catch ... finally,JavaScript也不例外。

try ... catch ... finally

使用try ... catch ... finally的执行流程。

当代码块被try { ... }包裹的时候,就表示这部分代码执行过程中可能会发生错误,一旦发生错误,就不再继续执行后续代码,转而跳到catch块。catch (e) { ... }包裹的代码就是错误处理代码,变量e表示捕获到的错误。最后,无论有没有错误,finally一定会被执行。

所以,有错误发生时,执行流程像这样:

  1. 先执行try { ... }的代码;
  2. 执行到出错的语句时,后续语句不再继续执行,转而执行catch (e) { ... }代码;
  3. 最后执行finally { ... }代码。

而没有错误发生时,执行流程像这样:

  1. 先执行try { ... }的代码;
  2. 因为没有出错,catch (e) { ... }代码不会被执行;
  3. 最后执行finally { ... }代码。

最后请注意,catch和finally可以不必都出现。也就是说,try语句一共有三种形式: 完整的try ... catch ... finally:

try {
    ...
} catch (e) {
    ...
} finally {
    ...
}

只有try ... catch,没有finally:

try {
    ...
} catch (e) {
    ...
}

只有try ... finally,没有catch:

try {
    ...
} finally {
    ...
}
错误类型

JavaScript有一个标准的Error对象表示错误,还有从Error派生的TypeError、ReferenceError等错误对象。我们在处理错误时,可以通过catch(e)捕获的变量e访问错误对象:

try {
    ...
} catch (e) {
    if (e instanceof TypeError) {
        alert('Type error!');
    } else if (e instanceof Error) {
        alert(e.message);
    } else {
        alert('Error: ' + e);
    }
}

使用变量e是一个习惯用法,也可以以其他变量名命名,如catch(ex)。

抛出错误

程序也可以主动抛出一个错误,让执行流程直接跳转到catch块。抛出错误使用throw语句。

例如,下面的代码让用户输入一个数字,程序接收到的实际上是一个字符串,然后用parseInt()转换为整数。当用户输入不合法的时候,我们就抛出错误:

Run

实际上,JavaScript允许抛出任意对象,包括数字、字符串。但是,最好还是抛出一个Error对象。

最后,当我们用catch捕获错误时,一定要编写错误处理语句:

let n = 0, s;
try {
    n = s.length;
} catch (e) {
    console.log(e);
}
console.log(n);

哪怕仅仅把错误打印出来,也不要什么也不干:

let n = 0, s;
try {
    n = s.length;
} catch (e) {
}
console.log(n);

因为catch到错误却什么都不执行,就不知道程序执行过程中到底有没有发生错误。

处理错误时,请不要简单粗暴地用alert()把错误显示给用户。教程的代码使用alert()是为了便于演示。

廖雪峰教程 · 8错误处理/8.1 错误传播

错误传播

如果代码发生了错误,又没有被try ... catch捕获,那么,程序执行流程会跳转到哪呢?

function getLength(s) {
    return s.length;
}

function printLength() {
    console.log(getLength('abc')); // 3
    console.log(getLength(null)); // Error!
}

printLength();

如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行。

所以,我们不必在每一个函数内部捕获错误,只需要在合适的地方来个统一捕获,一网打尽

当bar()函数传入参数null时,代码会报错,错误会向上抛给调用方foo()函数,foo()函数没有try ... catch语句,所以错误继续向上抛给调用方main()函数,main()函数有try ... catch语句,所以错误最终在main()函数被处理了。

至于在哪些地方捕获错误比较合适,需要视情况而定。

廖雪峰教程 · 8错误处理/8.2 异步处理

异步处理

编写JavaScript代码时,我们要时刻牢记,JavaScript引擎是一个事件驱动的执行引擎,代码总是以单线程执行,而回调函数的执行需要等到下一个满足条件的事件出现后,才会被执行。

例如,setTimeout()函数可以传入回调函数,并在指定若干毫秒后执行:

function printTime() {
    console.log('It is time!');
}

setTimeout(printTime, 1000);
console.log('done');

上面的代码会先打印done,1秒后才会打印It is time!。

如果printTime()函数内部发生了错误,我们试图用try包裹setTimeout()是无效的:

原因就在于调用setTimeout()函数时,传入的printTime函数并未立刻执行!紧接着,JavaScript引擎会继续执行console.log('done');语句,而此时并没有错误发生。直到1秒钟后,执行printTime函数时才发生错误,但此时除了在printTime函数内部捕获错误外,外层代码并无法捕获。

所以,涉及到异步代码,无法在调用时捕获,原因就是在捕获的当时,回调函数并未执行。

类似的,当我们处理一个事件时,在绑定事件的代码处,无法捕获事件处理函数的错误。